package lzy.impl.smallspring.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import lombok.Getter;
import lombok.Setter;
import lzy.impl.smallspring.beans.BeansException;
import lzy.impl.smallspring.beans.Config;
import lzy.impl.smallspring.beans.PropertyValue;
import lzy.impl.smallspring.beans.PropertyValues;
import lzy.impl.smallspring.beans.factory.config.AutowirableBeanFactory;
import lzy.impl.smallspring.beans.factory.config.BeanDefinition;
import lzy.impl.smallspring.beans.factory.config.BeanPostProcessor;
import lzy.impl.smallspring.beans.factory.config.BeanReference;
import lzy.impl.smallspring.beans.factory.support.instantiate.CglibSubclassingInstantiationStrategy;
import lzy.impl.smallspring.beans.factory.support.instantiate.InstantiationStrategy;
import lzy.impl.smallspring.beans.factory.support.instantiate.NativeInstantiationStrategy;

import java.lang.reflect.Constructor;
import java.util.Objects;

public abstract class AbstractAutowirableBeanFactory extends AbstractBeanFactory implements AutowirableBeanFactory {
    @Getter
    @Setter
    private InstantiationStrategy instantiationStrategy;

    {
        if (Objects.equals(Config.instantiationStrategy, "cglib")) {
            instantiationStrategy = new CglibSubclassingInstantiationStrategy();
        } else {
            instantiationStrategy = new NativeInstantiationStrategy();
        }
    }

    @Override
    protected Object createAndAddSingletonBean(String beanName, BeanDefinition definition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(definition, beanName, args);
            applyPropertyValues(beanName, bean, definition);
            initializeBean(beanName, bean, definition);
        } catch (Exception e) {
            throw new BeansException("Instantiation failed", e);
        }
        addSingleton(beanName, bean);
        return bean;
    }


    protected Object createBeanInstance(BeanDefinition beanDefinition, String name, Object[] args) {
        try {
            Constructor<?> matchedCtor = null;
            if (args != null && args.length != 0) {
                for (Constructor<?> constructor : beanDefinition.getBeanClass().getDeclaredConstructors()) {
                    Class<?>[] parameterTypes = constructor.getParameterTypes();
                    if (constructor.getParameterTypes().length != args.length) {
                        continue;
                    }
                    boolean matched = true;
                    for (int i = 0; i < args.length; i++) {
                        if (!parameterTypes[i].isInstance(args[i])) {
                            matched = false;
                            break;
                        }
                    }
                    if (matched) {
                        matchedCtor = constructor;
                        break;
                    }
                }
            }
            return instantiationStrategy.instantiate(beanDefinition, name, matchedCtor, args);


        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将bean reference引用转换成实际的bean
     *
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    private void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue property : propertyValues.getProperties()) {
                String name = property.getName();
                Object value = property.getValue();
                if (value instanceof BeanReference) {
                    // 从依赖获取实例
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (BeansException e) {
            throw new RuntimeException("Error setting property values：" + beanName);
        }
    }

    private Object initializeBean(String beanName, Object instantiatedBean, BeanDefinition beanDefinition) throws BeansException {
        Object processed = applyBeanPostProcessorsBeforeInitialization(instantiatedBean, beanName);

        // 在两段bpp之间的就是初始化，即Bean的Init方法
        // 在晚于chap6的内容中实现

        processed = applyBeanPostProcessorsAfterInitialization(processed, beanName);

        return processed;
    }

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object ret = existingBean;
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            Object processed = beanPostProcessor.postProcessBeforeInitialization(ret, beanName);
            if (processed == null) {
                return ret;
            }
            ret = processed;
        }
        return ret;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object ret = existingBean;
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            Object processed = beanPostProcessor.postProcessAfterInitialization(ret, beanName);
            if (processed == null) {
                return ret;
            }
            ret = processed;
        }
        return ret;
    }
}
